home *** CD-ROM | disk | FTP | other *** search
/ FishMarket 1.0 / FishMarket v1.0.iso / fishies / 126-150 / disk_128 / mrbackup / backup.c < prev    next >
C/C++ Source or Header  |  1992-05-06  |  17KB  |  682 lines

  1. /* MRBackup: Backup Routines
  2.  * Filename:    Backup.c
  3.  * Author:        Mark R. Rinfret
  4.  * Date:        09/04/87
  5.  *
  6.  * History:        (most recent change first)
  7.  *
  8.  * 09/04/87 -MRR- This package was (finally) extracted from Main.c.
  9.  */
  10.  
  11. #define MAIN
  12.  
  13. #include "MRBackup.h"
  14.  
  15.  
  16. /* Add a file/directory name to the list.
  17.  * Called with:
  18.  *        name:        filename string
  19.  *        FIB:        pointer to FileInfoBlock for this file or NULL.
  20.  *                    If NULL, file is starting directory.
  21.  *        list:        address of list header
  22.  * Returns:
  23.  *        0    => success, failure status otherwise
  24.  * Notes:
  25.  *        The filename string MUST NOT contain the volume component,
  26.  *        since it is used to build both source and destination
  27.  *        pathnames.  Also note that this routine inserts the name
  28.  *        into the list in sorted order (case sensitive).  Though this
  29.  *        changes the "natural order" of the files, it makes them 
  30.  *        easier to locate on the backup disks.
  31.  */
  32.  
  33. int 
  34. AddFile(name, FIB, list)
  35.     char *name; struct FileInfoBlock *FIB; T_FILE_LIST *list;
  36. {
  37.     USHORT blks;
  38.     T_FILE *fnode, *tnode;
  39.  
  40.  
  41.     /* See if the exclude list exists, and if it does, see if
  42.      * the name can be matched by one of the patterns.
  43.      */
  44.  
  45.     if (excludelist) {
  46.         if (CheckExclude(name)) {
  47.             sprintf(conmsg,"Excluding %s\n", name);
  48.             WriteConsole(conmsg);
  49.             return 0;
  50.         }
  51.     }
  52.  
  53.     if (!(fnode = (T_FILE *) calloc(1,sizeof(T_FILE)))) {
  54. nomem:
  55.         TypeAndSpeak("AddFile: Out of memory!\n");
  56.         return ERROR_NO_FREE_STORE;
  57.     }
  58.     if (!(fnode->filename = calloc(1, strlen(name)+1))) 
  59.         goto nomem;
  60.     strcpy(fnode->filename, name);        /* copy the filename */
  61.  
  62.     if (!FIB) {
  63.         blks = 1;
  64.         fnode->is_dir = true;
  65.     }
  66.     else if (fnode->is_dir = (FIB->fib_DirEntryType >= 0) )
  67.         blks = 1;                /* assume 1 block for directory */
  68.     else {
  69.         blks = ((FIB->fib_Size/488)+2);    /* compute file size in blocks */
  70.         blks += (blks/70);
  71.     }
  72.     fnode->blocks = blks;
  73.  
  74.     if (!list->first_file) {            /* file list is empty? */
  75.         list->first_file = fnode;        /* this is the new head */
  76.     }
  77.     else {
  78.         /* Find the insertion point for this file. */
  79.  
  80.         for (tnode = list->first_file; tnode; tnode = tnode->next) {
  81.             if (strcmp(fnode->filename, tnode->filename) <= 0) {
  82.                 fnode->next = tnode;    /* insert it here */
  83.                 if (tnode->previous)
  84.                     tnode->previous->next = fnode;
  85.                 fnode->previous = tnode->previous;
  86.                 tnode->previous = fnode;
  87.                 if (list->first_file == tnode)
  88.                     list->first_file = fnode;
  89.                 return 0;
  90.             }
  91.         }
  92.  
  93.         /* append to the end of the list */
  94.  
  95.         fnode->previous = list->last_file;
  96.         list->last_file->next = fnode;
  97.     }
  98.     list->last_file = fnode;
  99.     return 0;
  100. }
  101.  
  102. /* Main backup routine. */
  103.  
  104. Backup()
  105. {
  106.     int compare_flag, status = 0;
  107.  
  108.     Speak("O K,  I'm ready.  Lets back up your disk!");
  109.     DateStamp(now);
  110.     main_list.first_file = main_list.last_file = current_dir = NULL;
  111.  
  112.     if (do_listing) 
  113.         if ( OpenList(listpath) ) return;
  114.  
  115.     /* Get the file comparison date.  Only files created on or after
  116.      * this date are backed up.
  117.      */
  118.     do {
  119.         *since = *now;
  120.         since->ds_Days -= 1;        /* default interval is 1 day */
  121.         since->ds_Minute = 0;
  122.         since->ds_Tick = 0;
  123.         Speak("Enter the date since your last backup.");
  124.         DateRequest(mywindow, "Backup files since:",since, since);
  125.         if ( (compare_flag = CompareDS(since, now) ) >= 0) 
  126.             DisplayBeep(NULL);
  127.     } while (compare_flag >= 0);
  128.  
  129.     BreakPath(homepath, srcvol, srcpath);
  130.     BreakPath(backpath, destdrive, destpath);
  131.  
  132. #ifdef DEBUG
  133.     sprintf(debugmsg, "destdrive = %s, destpath = %s\n",destdrive,destpath);
  134.     DebugWrite(debugmsg);
  135.     sprintf(debugmsg, "srcvol = %s, srcpath = %s\n", srcvol,srcpath);
  136.     DebugWrite(debugmsg);
  137. #endif
  138.  
  139.     if (*destpath) {
  140.         TypeAndSpeak(
  141.             "On a backup, the backup path must only be a device name");
  142.         status = ERR_ABORT;
  143.         goto done;
  144.     }
  145.     strcat(destdrive,":");
  146.  
  147.     if (*excludepath)
  148.         if (GetExcludes()) goto done;
  149.  
  150.     level = 0;
  151.     back = 0;
  152.     size = 0;
  153.     if (*srcpath) {                /* starting path is a directory */
  154.         status = AddFile(srcpath, NULL, &main_list);
  155.     }
  156.     else                        /* starting path is a device */
  157.         status = CollectFiles(srcpath,&main_list);
  158.  
  159.     if (!status) {
  160.         while (main_list.first_file) {    /* while something in the list */
  161.             if (status = BackupFiles(&main_list))     break;
  162.             if (status = BackupDirs(&main_list))     break;
  163.         }
  164.     }
  165. done:
  166.     if (status == 0) {
  167.         TypeAndSpeak("That was a piece of cake.\n");
  168.     }
  169.     else {
  170.         sprintf(conmsg,"Oops!  Backup terminated with error %d.\n",status);
  171.         TypeAndSpeak(conmsg);
  172.     }
  173. }
  174.  
  175.  
  176. /* Process the next directory in the file list.
  177.  * This routine is recursive.
  178.  * Called with:
  179.  *        list:        file list to be processed
  180.  * Returns:
  181.  *        status code    (0 => success)
  182.  */
  183.  
  184. int
  185. BackupDirs(list)
  186.     T_FILE_LIST *list;
  187. {
  188.     T_FILE *dirnode;
  189.     T_FILE *saved_current_dir;
  190.     int     status = 0;
  191.     T_FILE_LIST sublist;                /* subdirectory list header */
  192.  
  193.     sublist.first_file = sublist.last_file = NULL;
  194.     saved_current_dir = current_dir;    /* remember current context */
  195.  
  196. /* There are a couple of things to note here.  The first is that once
  197.  * we have reached here, there should be NO simple file nodes in "list".
  198.  * That currently is not handled as an error, but probably should be.
  199.  * Second, since this scan modifies the list, a removal of a directory
  200.  * node starts the scan at the beginning of the list since we shouldn't
  201.  * reference the links in the node we're removing.  Since we should be
  202.  * removing the first node in the list anyway, who cares?
  203.  */
  204.     for (dirnode = list->first_file; dirnode; ) {
  205.         if (dirnode->is_dir) {            /* found one */
  206.             current_dir = dirnode;        /* set current directory */
  207.             RemFile(dirnode, list);
  208.             if (status = NewDir(current_dir->filename)) break;
  209.             if (status = CollectFiles(current_dir->filename,&sublist)) 
  210.                 break;
  211.             if (status = BackupFiles(&sublist)) break;
  212.             if (status = BackupDirs(&sublist))  break;
  213.             dirnode = list->first_file;
  214.         }
  215.         else                            /* should never get here !!! */
  216.             dirnode = dirnode->next;
  217.     }
  218.     current_dir = saved_current_dir;
  219.     return status;
  220. }
  221.  
  222. /* Backup all simple files in the current list.
  223.  * Called with:
  224.  *        list:        file list header
  225.  * Returns:
  226.  *        status code (0 => success)
  227.  */
  228. int
  229. BackupFiles(list)
  230.     T_FILE_LIST *list;
  231. {
  232.     T_FILE *primary, *secondary;
  233.     int status = 0;
  234.  
  235. /* The following loop continually scans the file list (from the front),
  236.  * looking for more file entries to process.  If the primary choice
  237.  * fails, an attempt is made to find a file which will fit in the
  238.  * space remaining.  If that attempt fails, then a new disk is formatted.
  239.  */
  240.  
  241.     while (primary = FindFile(list,false)) {/* find next file to process */
  242.         if (primary->blocks >= size) {     /* file doesn't fit */
  243.             if (!(secondary = FindFile(list,true))) {
  244.                 /* At this point, we know that there's at least one
  245.                  * file to back up, but none that fit.  Start a new
  246.                  * disk.
  247.                  */
  248.                 if (status = NewDisk(true)) return status;
  249.                 continue;                /* try that file again */
  250.             }
  251.             primary = secondary;        /* use second choice */
  252.         }
  253.         if (status = DoFile(primary)) return status;
  254.         RemFile(primary,list);            /* delete the node */
  255.     }
  256.     if (current_dir) {                    /* forget current directory */
  257.         FreeFile(current_dir);
  258.         current_dir = NULL;
  259.     }
  260.     return status;
  261. }
  262.  
  263. /* Check the file name about to be added against the exclude patterns.
  264.  * Called with:
  265.  *        name:    pathname to be checked
  266.  * Returns:
  267.  *        0 => no match
  268.  *        1 => name was matched, ignore it.
  269.  */
  270.  
  271. int
  272. CheckExclude(name)
  273.     char *name;
  274. {
  275.     int match = 0;
  276.     T_PATTERN *p;
  277.  
  278.  
  279.     for (p = excludelist; p; p = p->next_pattern) {
  280.         if (match = wildcmp(p->pattern, name)) break;
  281.     }
  282.     return match;
  283. }
  284.  
  285.  
  286. /* Check the current number of disk blocks (size) available.  If
  287.  * less than 1, it's time to format a new disk.
  288.  * Called with:
  289.  *        create_dir:        true => OK to create continuation directory
  290.  * Returns:
  291.  *        0 => success
  292.  *        1 => failure
  293.  */
  294.  
  295. int
  296. CheckSize(create_dir)
  297.     int create_dir;
  298. {
  299.     if (size > 0) return 0;
  300.     return NewDisk(create_dir);
  301. }
  302.  
  303. /* Collect file names from a starting path.
  304.  * Called with:
  305.  *        name:        starting pathname
  306.  *                        Note that name may be a null string when copying
  307.  *                        the entire home device.
  308.  *        list:        pointer to file list header
  309.  * Returns:
  310.  *        status code (0 => success)
  311.  * Notes:
  312.  *        CollectFiles attempts to collect all file and directory names
  313.  *        for a given level, starting with "name".  If a simple filename
  314.  *        is given as a starting path, only that name will be collected.
  315.  *        If a directory name is given, then all pathnames contained
  316.  *        within that directory (only) will be collected.  For each
  317.  *        directory name collected, CollectFiles will be called again to
  318.  *        collect files for that particular directory.  This iterative
  319.  *        approach (vs. recursive) saves memory and allows us to maintain
  320.  *        order at each directory level.
  321.  */
  322.  
  323. int
  324. CollectFiles(name, list)
  325.     char *name; T_FILE_LIST *list;
  326. {
  327.     int     status = 0;
  328.     struct FileInfoBlock   *FIB = NULL;
  329.     T_FILE *fnode;            /* file descriptor node */
  330.     struct Lock *lock = NULL;    
  331.     char path[PATH_MAX+1];
  332.     USHORT top_level;
  333.  
  334. #ifdef DEBUG
  335.     sprintf(debugmsg,"Collecting files from %s\n",name);
  336.     DebugWrite(debugmsg);
  337. #endif
  338.  
  339.     top_level = (*name == '\0');    /* empty name implies top level */
  340.  
  341.     if (!(FIB =
  342.         AllocMem((long)sizeof(struct FileInfoBlock),
  343.                  MEMF_CHIP|MEMF_CLEAR))) {
  344.         TypeAndSpeak("CollectFiles: Can not allocate memory for FIB\n");
  345.         return ERROR_NO_FREE_STORE;
  346.     }
  347.  
  348.     strcpy(path,srcvol);            /* rebuild current home path */
  349.     strcat(path,":");
  350.     strcat(path, name);
  351.  
  352.     if (!(lock=Lock(path,SHARED_LOCK))) {
  353.         status = IoErr();
  354.         sprintf(conmsg,"CollectFiles can not lock %s; error %d\n",
  355.             path, status);
  356.         TypeAndSpeak(conmsg);
  357.         goto out2;
  358.     }
  359.  
  360.     if ((Examine(lock,FIB))==0){
  361.         status = IoErr();
  362.         sprintf(conmsg,"CollectFiles can not examine %s; error: %d\n",
  363.                 name, status);
  364.         TypeAndSpeak(conmsg);
  365.         goto out2;
  366.     }
  367.  
  368.     if (FIB->fib_DirEntryType > 0){    /* "name" is a directory */
  369.  
  370.         while(!status && ExNext(lock,FIB)) {
  371.             if (FIB->fib_DirEntryType < 0) {
  372.                 if (CompareDS(&FIB->fib_Date, since) < 0) {
  373. #ifdef DEBUG
  374.                     sprintf(debugmsg,"Skipping %s\n",
  375.                             &FIB->fib_FileName[0]);
  376.                     DebugWrite(debugmsg);
  377. #endif
  378.                     continue;
  379.                 }
  380.             }
  381.             if (top_level)
  382.                 *path = '\0';
  383.             else {
  384.                 strcpy(path,name);
  385.                 strcat(path,"/");
  386.             }
  387.             strcat(path,&FIB->fib_FileName[0]);
  388.             status = AddFile(path, FIB, list);
  389.         }
  390.         /* !!! Need check here for ERROR_NO_MORE_ENTRIES */
  391.     }
  392.     else {
  393.         if ( CompareDS(&FIB->fib_Date, since ) >= 0 ) 
  394.             status = AddFile(name, FIB, list);
  395.         }
  396. out2: 
  397.     UnLock(lock);
  398. out1: 
  399.     FreeMem(FIB, (long)sizeof(struct FileInfoBlock) );
  400.  
  401.     /* Don't give up if somebody else is using the file - just
  402.      * ignore the error.  The user can cancel us if there's a
  403.      * problem.
  404.      */
  405.  
  406.     if (status == ERROR_OBJECT_IN_USE)
  407.         status = 0;
  408.     return status;
  409. }
  410.  
  411. /* Process one file.
  412.  * Called with:
  413.  *        fnode:            node describing file
  414.  * Returns:
  415.  *        0    => success
  416.  *        1    => failure
  417.  */
  418. int
  419. DoFile(fnode)
  420.     T_FILE *fnode;
  421. {
  422. #define MAX_RETRY    3
  423.  
  424.     int     status = ERR_NONE;
  425.     char    destname[PATH_MAX+1];
  426.     int        oldsize;
  427.     USHORT     retries = 0;
  428.     char    srcname[PATH_MAX+1];
  429.  
  430.     oldsize = size;
  431.     size -= fnode->blocks;            /* deduct blocks for file */
  432.     if (CheckSize(true))            /* check disk space */
  433.         return ERR_ABORT;
  434.  
  435.     if (size > oldsize)                /* we just formatted */
  436.         oldsize = size;
  437.  
  438. /*#define NOCOPY*/                        /* for fast debugging */
  439.     do {
  440.         if (status = CheckStop())    /* does user want out? */
  441.             return status;
  442.  
  443.         sprintf(srcname,"%s:%s",srcvol,fnode->filename);
  444.         sprintf(conmsg,"Blocks left: %5d   ",oldsize);
  445.         WriteConsole(conmsg);
  446.         if ( !do_compress || IsCompressed(srcname) || fnode->blocks < 4){
  447.             sprintf(destname,"%s%s",destvol,fnode->filename);
  448.             sprintf(conmsg,"Copying %s\n",srcname);
  449.             WriteConsole(conmsg);
  450. #ifndef NOCOPY
  451.             status = CopyFile(srcname,destname);
  452. #endif
  453.         }
  454.         else {
  455.             sprintf(destname,"%s%s.Z",destvol,fnode->filename);
  456.             sprintf(conmsg,"Compressing %s\n",srcname);
  457.             WriteConsole(conmsg);
  458. #ifndef NOCOPY
  459.             if (!(status = compress(srcname,destname)))
  460.                 status = CopyFileDate(srcname,destname);
  461. #endif
  462.         }
  463.         if (status) {
  464. /*
  465.             status = GetErrOpt("I/O error during copy or compress",
  466.                         ERR_ABORT | ERR_IGNORE | 
  467.                         ERR_RETRY_FILE | ERR_RESTART_VOLUME);
  468. */
  469.             sprintf(conmsg,"!!!I/O error %d on file %s",status,destname);
  470.             ListLine(conmsg);
  471.             unlink(destname);
  472.             if (++retries <= MAX_RETRY) {
  473.                 strcat(conmsg," - retrying this file.\n");
  474.                 if (retries == MAX_RETRY) size = 0; /* try new output disk */
  475.                 status = ERR_RETRY_FILE;
  476.             }
  477.             else {
  478.                 strcat(conmsg," - aborting this file.\n");
  479.  
  480. /* Note that for now, we will proceed by brute force.  A later revision
  481.  * of this program will provide context-saving so we can restart the
  482.  * series of files on this floppy.
  483.  */
  484.                 status = ERR_IGNORE;
  485.             }
  486.             WriteConsole(conmsg);
  487.         }
  488.         else
  489.             ListLine(destname);
  490.  
  491.     } while (status == ERR_RETRY_FILE && (retries < MAX_RETRY));
  492.     if (status == ERR_IGNORE)
  493.         status = ERR_NONE;
  494.  
  495. #ifndef NOCOPY
  496.     if (!status){
  497.         if ((size = DiskBlocks(destvol)) < 0)    /* update blocks left */
  498.             ++status;
  499.     }
  500. #endif
  501.     return status;
  502. }
  503.  
  504. /* Attempt to find a file node in the file list.  If the check_size
  505.  * parameter is true, only look at files which will fit in the space
  506.  * remaining on the disk.
  507.  * Called with:
  508.  *        list:        pointer to file list to be searched
  509.  *        check_size: false => find first file in the list
  510.  *                    true  => test space available
  511.  * Returns:
  512.  *        file node or NULL (not found)
  513.  */
  514.  
  515. T_FILE *FindFile(list,check_size)
  516.     T_FILE_LIST *list; int check_size;
  517. {
  518.     T_FILE *fnode;
  519.  
  520.     for (fnode = list->first_file; fnode; fnode = fnode->next) {
  521.         if (!fnode->is_dir) {        /* don't consider directory nodes */
  522.             if (!check_size) break;    /* take this one */
  523.             if (fnode->blocks < size) break;
  524.         }
  525.     }
  526.     return fnode;
  527. }
  528.  
  529. /* Free memory allocated to a file node.
  530.  * Called with:
  531.  *        node:    file node
  532.  */
  533. FreeFile(node)
  534.     T_FILE *node;
  535. {
  536.  
  537.     free(node->filename);
  538.     free(node);
  539. }
  540.  
  541. /* An exclude file pathname has been specified.  Get the patterns it
  542.  * contains.
  543.  * Returns:
  544.  *        status:    0 => success, failure otherwise
  545.  */
  546. int
  547. GetExcludes()
  548. {
  549.     USHORT i, length, nonwild;
  550.     T_PATTERN *p;
  551.     int status = 0;
  552.     char str[PATH_MAX+1];
  553.     FILE *xcld;
  554.  
  555.     if (! exclude_has_changed)
  556.         return 0;
  557.  
  558.     if (!(xcld = fopen(excludepath, "r"))) {
  559.         sprintf(conmsg, 
  560.             "I couldn't open the exclude pattern file, error %d.", errno);
  561.         TypeAndSpeak(conmsg);
  562.         return errno;
  563.     }
  564.  
  565.     /* Release any previous exclude list. */
  566.  
  567.     for (p = excludelist; p; p = p->next_pattern) {
  568.         free(p->pattern);
  569.         free(p);
  570.     }
  571.     excludelist = lastexclude = NULL;
  572.  
  573.     while (fgets(str, PATH_MAX, xcld)) {
  574.         if (length = strlen(str)) {
  575.             --length;
  576.             str[length] = '\0';
  577.         }
  578.         if (length && *str != '#') {    /* ignore blank lines and comments */
  579.             nonwild = 0;
  580.             for (i = 0; i < length; ++i) {
  581.                 if (str[i] != '*' && str[i] != '?' && str[i] != '/')
  582.                     ++nonwild;
  583.             }
  584.             if (! nonwild ) {
  585.                 sprintf(conmsg,
  586.                 "Very funny!  %s will exclude everything!  Ha ha ha!\n",
  587.                     str);
  588.                 TypeAndSpeak(conmsg);
  589.                 status = 1;
  590.                 goto done;
  591.             }
  592.             p = (T_PATTERN *) calloc(sizeof(T_PATTERN), 1);
  593.             p->pattern = calloc(length+1, 1);
  594.             strcpy(p->pattern, str);
  595.             if ( ! excludelist ) 
  596.                 excludelist = p;
  597.             else
  598.                 lastexclude->next_pattern = p;
  599.             lastexclude = p;
  600.         }
  601.     }
  602. done:
  603.     fclose(xcld);
  604.     if (! status )
  605.         exclude_has_changed = false;
  606.     return status;
  607. }
  608.  
  609. /* Format a new diskette.
  610.  * Called with:
  611.  *        create_dir:    true => create continuation directory, if necessary
  612.  * Returns:
  613.  *        false => success
  614.  *        true  => failure
  615.  */
  616.  
  617. int
  618. NewDisk(create_dir)
  619.     int create_dir;
  620. {
  621.     char datestr[20];
  622.     int status = 0;
  623.  
  624.     if (back)                        /* not first disk? */
  625.         Delay(TICKS_PER_SECOND * 5L);    /* let disk buffers flush */
  626.  
  627.     ++back;                    /* Increment the volume ID */
  628.     Speak("Attention!  I need a disk for formatting.");
  629.     do {
  630.         if (!RequestDisk(mywindow, destdrive))
  631.             return ERR_ABORT;
  632.  
  633.          /* Don't put the colon in the volume name - 
  634.          * FormatDisk will happily use it as part of 
  635.          * the name rather than as a delimiter. 
  636.          */
  637.  
  638.         DS2Str(datestr, "%02m-%02d-%02y", now);
  639.         sprintf(destvol,"Backup %s.%d", datestr, back);
  640.  
  641.         Inhibit(destdrive,1);
  642.         if (status = FormatDisk(destdrive,destvol)) {
  643.             TypeAndSpeak("I failed to format the disk.  Sorry.\n");
  644.         }
  645.     } while (status);
  646.  
  647.     strcat(destvol, ":");            /* add colon */
  648.  
  649.     if ((size = DiskBlocks(destvol)) < 0)
  650.         status = -size;
  651.     else
  652.         if (create_dir && (current_dir != NULL) )
  653.             status = NewDir(current_dir->filename);
  654.  
  655.     return status;
  656. }
  657.  
  658. /* Remove a file node from the list.
  659.  * Called with:
  660.  *        node:        file node pointer
  661.  *        list:        file list header
  662.  * Returns:
  663.  *        nothing
  664.  */
  665. RemFile(node,list)
  666.     T_FILE *node; T_FILE_LIST *list;
  667. {
  668.     if (node->previous)
  669.         node->previous->next = node->next;
  670.  
  671.     if (node->next)
  672.         node->next->previous = node->previous;
  673.  
  674.     if (node == list->first_file)
  675.         list->first_file = node->next;
  676.  
  677.     if (node == list->last_file)
  678.         list->last_file = node->previous;
  679.  
  680.     if (!node->is_dir) FreeFile(node);
  681. }
  682.